from colorama import Fore, Style, init, Back
from os import path as pt
from os import system
from json import load
import requests
import sys
import threading
import queue
import time

def send_log(type: str = 'info', message: str = '', pid: str = None, submit_id: str = None) -> None:
    try:
        global log_mode
        
        formatted_message = message
        if pid:
            formatted_message = f"{formatted_message} -> {pid}"
        if submit_id:
            formatted_message = f"{formatted_message} -> {submit_id}"
            
        match type:
            case 'info':
                if log_mode == 1:
                    print(formatted_message)
                elif log_mode == 2:
                    requests.post('http://127.0.0.1:1146/api/ban_account/log', json={'type': 'info', 'message': formatted_message})
            case 'success':
                if log_mode == 1:
                    print(Fore.GREEN + formatted_message + Style.RESET_ALL)
                elif log_mode == 2:
                    requests.post('http://127.0.0.1:1146/api/ban_account/log', json={'type': 'success', 'message': formatted_message})
            case 'warning':
                if log_mode == 1:
                    print(Fore.YELLOW + formatted_message + Style.RESET_ALL)
                elif log_mode == 2:
                    requests.post('http://127.0.0.1:1146/api/ban_account/log', json={'type': 'warning', 'message': formatted_message})
            case 'error':
                if log_mode == 1:
                    print(Fore.RED + formatted_message + Style.RESET_ALL)
                elif log_mode == 2:
                    requests.post('http://127.0.0.1:1146/api/ban_account/log', json={'type': 'error', 'message': formatted_message})
            case 'debug':
                if log_mode == 1:
                    print(Fore.BLUE + formatted_message + Style.RESET_ALL)
                elif log_mode == 2:
                    requests.post('http://127.0.0.1:1146/api/ban_account/log', json={'type': 'debug', 'message': formatted_message})
    except: 
        return

# 初始化颜色
init(autoreset=True)

# 定义日志模式 (1 控制台, 2 Web API)
log_mode = 1

# 导入配置
user_data_path = pt.join(pt.dirname(pt.normpath(sys.argv[0])), 'user_data.json')
is_user_data_read = True
user_data = None
url = None

try:
    with open(user_data_path, 'r', encoding='utf-8') as file:
        user_data = load(file)
        url = user_data['OJ']['APIURL']
except FileNotFoundError:
    is_user_data_read = False
    send_log('error', f'未找到配置文件 {user_data_path}')
except Exception as e:
    is_user_data_read = False
    send_log('error', f'读取用户数据时发生错误：{e}')

# 封号函数
def ban(username) -> None:
    for i in range(25):
        if login(username, 'Attack from HOJ Tool') == '对不起！登录失败次数过多！您的账号有风险，半个小时内暂时无法登录！':
            send_log('success', f"{username}封禁成功！")
            return
    send_log('warning', f"{username}可能是空号号不存在或平台禁用封禁。")

# 线程工作函数
def worker_thread(username_queue, stop_event):
    while not stop_event.is_set():
        try:
            username = username_queue.get_nowait()
        except queue.Empty:
            break
            
        if stop_event.is_set():
            break
            
        ban(username)
        username_queue.task_done()

# 线程池管理函数
def process_with_threads(usernames, thread_count=5):
    username_queue = queue.Queue()
    stop_event = threading.Event()
    threads = []
    
    # 将用户名添加到队列
    for username in usernames:
        username_queue.put(username)
    
    # 创建并启动线程
    for _ in range(thread_count):
        thread = threading.Thread(target=worker_thread, args=(username_queue, stop_event))
        thread.daemon = True
        thread.start()
        threads.append(thread)
    
    # 等待所有任务完成或停止事件
    try:
        while not username_queue.empty() and not stop_event.is_set():
            # 检查停止标志
            if log_mode == 2:
                try:
                    status_response = requests.get("http://127.0.0.1:1146/api/ban_account/status", timeout=2)
                    if status_response.json().get('stop_flag') == True:
                        stop_event.set()
                        requests.get("http://127.0.0.1:1146/api/ban_account/stopp")
                        break
                except:
                    pass
            time.sleep(0.1)
    except KeyboardInterrupt:
        stop_event.set()
    
    # 等待所有线程完成
    for thread in threads:
        thread.join(timeout=1)
    
    return stop_event.is_set()

def login(username, password):
    headers = {
        'Accept': 'application/json',
        'Content-Type': 'application/json',
        'Url-Type': 'general',
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36',
    }

    data = {
        'username': f'{username}',
        'password': f'{password}',
    }

    response = requests.post(f'{url}/api/login', headers=headers, json=data)
    return response.json()['msg']

def ban_account(mode: str, arg=None) -> None:
    global log_mode
    if mode == 'all':
        if arg is None:
            log_mode = 1
            white_list = input('请输入封禁白名单，以逗号分隔: ').split(',')
        else:
            log_mode = 2
            white_list = arg.split(',')
            
        headers = {
            'Accept': 'application/json',
            'Content-Type': 'application/json', 
            'Url-Type': 'general', 
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36',
        } 
        
        try:
            response = requests.get(f'{url}/api/get-rank-list?currentPage=1&limit=114514&type=1', headers=headers)
        except requests.RequestException as e:
            send_log('error', f"获取排行榜请求失败: {e}")
            return
        
        # 检查响应状态码 
        if response.status_code != 200:
            send_log('error', f"获取排行榜请求失败，状态码：{response.status_code}")
            return
        
        try:
            json_data = response.json() 
        except requests.exceptions.JSONDecodeError:
            send_log('error', f"排行榜请求JSON解析失败。原始响应：{response.text}")
            return
        
        # 检查JSON结构 
        if 'data' not in json_data or 'records' not in json_data['data']:
            send_log('error', f"获取排行榜响应JSON结构不符合预期：{json_data}")
            return
        
        # 收集需要封禁的用户名
        usernames_to_ban = []
        for i in json_data['data']['records']: 
            if log_mode == 2:
                try:
                    status_response = requests.get("http://127.0.0.1:1146/api/ban_account/status", timeout=2)
                    if status_response.json().get('stop_flag') == True:
                        requests.get("http://127.0.0.1:1146/api/ban_account/stopp")
                        return
                except:
                    pass
            
            if i['username'] in white_list:
                continue
            usernames_to_ban.append(i['username'])
        
        # 使用线程池处理封禁
        if usernames_to_ban:
            send_log('info', f"开始使用5线程封禁 {len(usernames_to_ban)} 个用户...")
            stopped = process_with_threads(usernames_to_ban, 5)
            if stopped:
                send_log('warning', "封禁过程被用户停止")
            else:
                send_log('success', f"已完成封禁 {len(usernames_to_ban)} 个用户")
        else:
            send_log('info', "没有需要封禁的用户")
            
    elif mode == 'assign':
        if arg is None:
            username = input('封禁账号名称，用逗号分隔:').split(',')
            log_mode = 1
        else:
            username = arg.split(',')
            log_mode = 2
            
        send_log('info', f"开始使用5线程封禁指定用户：{', '.join(username)}")
        
        # 使用线程池处理封禁
        if username:
            stopped = process_with_threads(username, 5)
            if stopped:
                send_log('warning', "封禁过程被用户停止")
            else:
                send_log('success', f"已完成封禁 {len(username)} 个指定用户")
        else:
            send_log('info', "没有需要封禁的用户")
    if log_mode == 2:
        requests.get("http://127.0.0.1:1146/api/ban_account/stopp")

def main() -> None:
    if is_user_data_read:
        mode = None
        while mode != 3:
            system('cls')
            print(Fore.BLUE + '欢迎使用HOJtool-一键封号！\n作者：Jonsin\nGitee仓库: https://gitee.com/wzokee/hoj-tool\n' + Back.RED + Fore.WHITE + '仅供参考学习!' + Style.RESET_ALL + Fore.GREEN + '\n\n请选择模式:' + Fore.CYAN + '\n1.封所有账户\n2.封指定账户\n3.退出\n' + Style.RESET_ALL)
            mode = input('请输入序号:')
            system('cls')
            if is_user_data_read:
                if mode == '1':
                    ban_account(mode='all')
                    send_log('success', '已退出')
                    system('pause')
                elif mode == '2':
                    ban_account(mode='assign')
                    send_log('success', '已退出')
                    system('pause')
            else:
                send_log('error', '请先配置用户数据。')
                system('pause')
            if mode == '3':
                return
    else:
        send_log('error', '请先配置用户数据。')
        system('pause')

if __name__ == '__main__':
    main()